{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Ic4_occAAiAT"
   },
   "source": [
    "##### Copyright 2018 The TensorFlow Authors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "ioaprt5q5US7"
   },
   "outputs": [],
   "source": [
    "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
    "# you may not use this file except in compliance with the License.\n",
    "# You may obtain a copy of the License at\n",
    "#\n",
    "# https://www.apache.org/licenses/LICENSE-2.0\n",
    "#\n",
    "# Unless required by applicable law or agreed to in writing, software\n",
    "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
    "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
    "# See the License for the specific language governing permissions and\n",
    "# limitations under the License."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "cellView": "form",
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "yCl0eTNH5RS3"
   },
   "outputs": [],
   "source": [
    "#@title MIT License\n",
    "#\n",
    "# Copyright (c) 2017 François Chollet\n",
    "#\n",
    "# Permission is hereby granted, free of charge, to any person obtaining a\n",
    "# copy of this software and associated documentation files (the \"Software\"),\n",
    "# to deal in the Software without restriction, including without limitation\n",
    "# the rights to use, copy, modify, merge, publish, distribute, sublicense,\n",
    "# and/or sell copies of the Software, and to permit persons to whom the\n",
    "# Software is furnished to do so, subject to the following conditions:\n",
    "#\n",
    "# The above copyright notice and this permission notice shall be included in\n",
    "# all copies or substantial portions of the Software.\n",
    "#\n",
    "# THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n",
    "# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n",
    "# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL\n",
    "# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n",
    "# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n",
    "# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n",
    "# DEALINGS IN THE SOFTWARE."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "ItXfxkxvosLH"
   },
   "source": [
    "# Классификация текста обзоров фильмов"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hKY4XMc9o8iB"
   },
   "source": [
    "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://www.tensorflow.org/tutorials/keras/basic_text_classification\"><img src=\"https://www.tensorflow.org/images/tf_logo_32px.png\" />Читай на TensorFlow.org</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ru/tutorials/keras/basic_text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />Запусти в Google Colab</a>\n",
    "  </td>\n",
    "  <td>\n",
    "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ru/tutorials/keras/basic_text_classification.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />Изучай код на GitHub</a>\n",
    "  </td>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "Eg62Pmz3o83v"
   },
   "source": [
    "\n",
    "В этом интерактивном уроке мы построим модель, которая будет классифицировать обзор фильма как *позитивный* или *негативный* на основе текста. Это пример *бинарной* классификации (по двум классам), важной, и широко применяющейся задачи машинного обучения.\n",
    "\n",
    "Мы воспользуемся [датасетом IMDB](https://www.tensorflow.org/api_docs/python/tf/keras/datasets/imdb), который содержит тексты 50,000 обзоров фильмов из [Internet Movie Database](https://www.imdb.com/). Они разделены на 25,000 обзоров для обучения, и 25,000 для проверки модели. Тренировочные и проверочные датасеты *сбалансированы*, т.е. содержат одинаковое количество позитивных и негативных обзоров.\n",
    "\n",
    "Данное руководство использует [tf.keras](https://www.tensorflow.org/guide/keras), высокоуровневый API для создания и обучения моделей в TensorFlow. Чтобы сделать более сложную по структуре классификацую текста при помощи `tf.keras`, читай [Руководство по классификации текстов](https://developers.google.com/machine-learning/guides/text-classification/)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "2ew7HTbPpCJH"
   },
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "from tensorflow import keras\n",
    "\n",
    "import numpy as np\n",
    "\n",
    "print(tf.__version__)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "iAsKG535pHep"
   },
   "source": [
    "## Загружаем датасет IMDB\n",
    "\n",
    "Датасет IMDB доступен сразу в TensorFlow при помощи метода `load_data`. Он уже подготовлен таким образом, что обзоры (последовательности слов) были конвертированы в последовательность целых чисел, где каждое целое представляет конкретное слово в массиве.\n",
    "\n",
    "Давай напишем пару строчек кода чтобы загрузить датасет (или автоматически используем копию из кэша, если ты уже скачал этот набор данных):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "zXXx5Oc3pOmN"
   },
   "outputs": [],
   "source": [
    "imdb = keras.datasets.imdb\n",
    "\n",
    "(train_data, train_labels), (test_data, test_labels) = imdb.load_data(num_words=10000)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "odr-KlzO-lkL"
   },
   "source": [
    "Аргумент `num_words=10000` позволяет нам ограничиться только 10,000 наиболее часто встречающимися словами из тренировочного сета. Все редкие слова исключаются. Это поможет нам держать объем данных в разумных пределах."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "l50X3GfjpU4r"
   },
   "source": [
    "## Знакомимся с данными\n",
    "\n",
    "Давай посмотрим какая информация нам доступна. Данные уже подготовлены: каждый пример - это массив целых чисел, которые представляют слова из обзоров. Каждая метка *label* является целым числом 0 или 1: \n",
    "\n",
    "0 - негативный обзор, 1 - позитивный."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "y8qCnve_-lkO"
   },
   "outputs": [],
   "source": [
    "print(\"Training entries: {}, labels: {}\".format(len(train_data), len(train_labels)))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "RnKvHWW4-lkW"
   },
   "source": [
    "Текст обзоров уже был конвертирован в целые числа, где каждое целок представляет слово из словаря. Вот пример того, как выглядит первый обзор:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "QtTS4kpEpjbi"
   },
   "outputs": [],
   "source": [
    "print(train_data[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hIE4l_72x7DP"
   },
   "source": [
    "Разные обзоры фильмов также имеют разное количество слов. Код ниже поможет нам узнать количество слов в первом и втором обзоре. Поскольку нейросеть может принимать только данные одинаковой длины, то нам предстоит как-то решить эту задачу."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "X-6Ii9Pfx6Nr"
   },
   "outputs": [],
   "source": [
    "len(train_data[0]), len(train_data[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "4wJg2FiYpuoX"
   },
   "source": [
    "### Конвертируем целые обратно в слова\n",
    "\n",
    "Не будет лишним также знать, как конвертировать целые числа из массива обратно в текст. Напишем вспомогательную функцию, с помощью который мы сможем запрашивать из этого словаря объект, который содержит указанные числа и отображать их в виде слов:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "tr5s_1alpzop"
   },
   "outputs": [],
   "source": [
    "# Назначим словарь, который будет отображать слова из массива данных\n",
    "word_index = imdb.get_word_index()\n",
    "\n",
    "# Зарезервируем первые несколько значений\n",
    "word_index = {k:(v+3) for k,v in word_index.items()} \n",
    "word_index[\"<PAD>\"] = 0\n",
    "word_index[\"<START>\"] = 1\n",
    "word_index[\"<UNK>\"] = 2  # Вместо редких слов, не вошедших в набор из 10,000, будет указано UNK\n",
    "word_index[\"<UNUSED>\"] = 3\n",
    "\n",
    "reverse_word_index = dict([(value, key) for (key, value) in word_index.items()])\n",
    "\n",
    "def decode_review(text):\n",
    "    return ' '.join([reverse_word_index.get(i, '?') for i in text])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "U3CNRvEZVppl"
   },
   "source": [
    "Теперь мы можем легко воспользоваться функцией `decode_review` для отображения текста первого обзора фильма:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "s_OqxmH6-lkn"
   },
   "outputs": [],
   "source": [
    "decode_review(train_data[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "lFP_XKVRp4_S"
   },
   "source": [
    "## Подготавливаем данные\n",
    "\n",
    "Обзоры фильмов из массива целых чисел должны быть конвертированы в тензоры прежде, чем они будут пропущены через нейросеть. Эта конвертация может быть сделана несколькими способами:\n",
    "\n",
    "* *One-hot encoding* конвертирует массивы в векторы 0 и 1. Например, последовательность [3, 5] станет 10,000-мерным вектором, полностью состоящим из нулей кроме показателей 3 и 5, которые будут представлены единицами. Затем, нам нужно будет создать первый `Dense` слой в нашей сети, который сможет принимать векторые данные с плавающей запятой. Такой подход очень требователен к объему памяти, несмотря на то, что требует указать размеры матрицы `num_words * num_reviews`\n",
    "\n",
    "* Другой способ - сделать все массивы одинаковыми по длине, а затем создать тензор целых чисел с указанием `max_length * num_reviews`. Мы можем использовать *Embedding* (пер. \"Встроенный\") слой, который может использовать эти параметры в качестве первого слоя нашей сети\n",
    "\n",
    "В этом руководстве мы попробуем второй способ.\n",
    "\n",
    "Поскольку все обзоры фильмов должны быть одинаковой длины, то мы используем функцию [pad_sequences](https://www.tensorflow.org/api_docs/python/tf/keras/preprocessing/sequence/pad_sequences), чтобы привести все длины к одному значению:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "2jQv-omsHurp"
   },
   "outputs": [],
   "source": [
    "train_data = keras.preprocessing.sequence.pad_sequences(train_data,\n",
    "                                                        value=word_index[\"<PAD>\"],\n",
    "                                                        padding='post',\n",
    "                                                        maxlen=256)\n",
    "\n",
    "test_data = keras.preprocessing.sequence.pad_sequences(test_data,\n",
    "                                                       value=word_index[\"<PAD>\"],\n",
    "                                                       padding='post',\n",
    "                                                       maxlen=256)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "VO5MBpyQdipD"
   },
   "source": [
    "Давай теперь посмотрим на длину наших примеров:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "USSSBnkE-lky"
   },
   "outputs": [],
   "source": [
    "len(train_data[0]), len(train_data[1])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "QJoxZGyfjT5V"
   },
   "source": [
    "А также проверим как выглядит первый стандартизированный по длине обзор фильма:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "TG8X9cqi-lk9"
   },
   "outputs": [],
   "source": [
    "print(train_data[0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "LLC02j2g-llC"
   },
   "source": [
    "## Строим модель\n",
    "\n",
    "Нейронная сеть создается посредством стека (наложения) слоев - это требует ответов на два вопроса об архитектуре самой модели:\n",
    "\n",
    "* Сколько слоев будет использовано в модели?\n",
    "* Сколько *скрытых блоков* будет использовано для каждого слоя?\n",
    "\n",
    "В этом примере, входные данные состоят из массива слов (целых чисел). Получаемые предсказания будут в виде меток 0 или 1. Давай построим модель, которая будет решать нашу задачу:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "xpKOoWgu-llD"
   },
   "outputs": [],
   "source": [
    "# Размер входных данных - количество слов, использованных в обзорах фильмов (10,000 слов)\n",
    "vocab_size = 10000\n",
    "\n",
    "model = keras.Sequential()\n",
    "model.add(keras.layers.Embedding(vocab_size, 16))\n",
    "model.add(keras.layers.GlobalAveragePooling1D())\n",
    "model.add(keras.layers.Dense(16, activation=tf.nn.relu))\n",
    "model.add(keras.layers.Dense(1, activation=tf.nn.sigmoid))\n",
    "\n",
    "model.summary()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "6PbKQ6mucuKL"
   },
   "source": [
    "Для создания классификатора все слои проходят процесс стека, или наложения:\n",
    "\n",
    "1. Первый `Embedding` слой принимает переведенные в целые числа слова и ищет соответствующий вектор для каждой пары слово/число. Модель обучается на этих векторах. Векторы увеличивают размер получаемого массива на 1, в результате чего мы получаем измерения: `(batch, sequence, embedding)`\n",
    "\n",
    "2. Следующий слой `GlobalAveragePooling1D` возвращает получаемый вектор заданной длины для каждого примера, усредняя размер ряда. Это позволит модели легко принимать данные разной длины\n",
    "\n",
    "3. Этот вектор пропускается через полносвязный `Dense` слой с 16 скрытыми блоками\n",
    "\n",
    "4. Последний слой также является полносвязным, но с всего одним выходящим нодом. При помощи функции активации `sigmoid` (Сигмоида) мы будем получать число с плавающей запятой между 0 и 1, которое будет показывать вероятность или уверенность модели"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "0XMwnDOp-llH"
   },
   "source": [
    "### Скрытые блоки\n",
    "\n",
    "Вышеописанная модель имеет 2 промежуточных или *скрытых* слоя, между входом и выходом данных. Количество выходов (блоков, нодов или нейронов) является размером репрезентативного пространства слоя. Другими словами, количество свободы, которая разрешена сети во время обучения.\n",
    "\n",
    "Если модель имеет больше скрытых блоков, и/или больше слоев, то тогда нейросеть может обучиться более сложным представлениям. Однако в этом случае это будет дороже с точки зрения вычислительных ресурсов и может привести к обучению нежелательных паттернов - паттернов, которые улучшают показатели на тренировочных данных, но не на проверочных. Это называется *переобучением*, и мы обязательно познакомимся с этим явлением далее."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "L4EqVWg4-llM"
   },
   "source": [
    "### Функция потерь и оптимизатор\n",
    "\n",
    "Для модели нам необходимо указать функцию потерь и оптимизатор для обучения. Поскольку наша задача является примером бинарной классификации и модель будет показывать вероятность (слой из единственного блока с сигмоидой в качестве функции активации), то мы воспользуемся функцией потерь `binary_crossentropy` (пер. \"Перекрестная энтропия\").\n",
    "\n",
    "Это не единственный выбор для нашей функции потерь: ты можешь, например, выбрать `mean_squared_error`. Но обычно `binary_crossentropy` лучше справляется с вероятностями - она измеряет \"дистанцию\" между распределениями вероятностей, или, как в нашем случае, между эталоном и предсказаниями.\n",
    "\n",
    "Далее, по мере знакомства с задачами регрессии (например, предсказание цен на недвижимость), мы посмотрим как использовать другую функцию потерь, которая называется среднеквадратичская ошибка (MSE).\n",
    "\n",
    "А сейчас, настроим нашу модель: мы будем использовать *оптимизатор Адама* и *перекрестную энтропию* для потерь:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "Mr0GP-cQ-llN"
   },
   "outputs": [],
   "source": [
    "model.compile(optimizer=tf.train.AdamOptimizer(),\n",
    "              loss='binary_crossentropy',\n",
    "              metrics=['accuracy'])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "hCWYwkug-llQ"
   },
   "source": [
    "## Создадим проверочный набор данных\n",
    "\n",
    "Во время обучения мы хотим проверить точность нашей модели на данных, которых она еще не видела. Давай создадим *проверочный сет* данных, выделив 10,000 примеров из оригинального тренировочного сета в отдельный набор.\n",
    "\n",
    "Почему мы не используем проверочный набор прямо сейчас? Наша цель - разработать и настроить нашу модель, используя только данные для обучения, и только потом использовать проверочный сет всего один раз чтобы оценить точность."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "-NpcXY9--llS"
   },
   "outputs": [],
   "source": [
    "x_val = train_data[:10000]\n",
    "partial_x_train = train_data[10000:]\n",
    "\n",
    "y_val = train_labels[:10000]\n",
    "partial_y_train = train_labels[10000:]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "35jv_fzP-llU"
   },
   "source": [
    "## Обучаем модель\n",
    "\n",
    "Начнем тренировку нашей модели с 40 эпох при помощи мини-батчей по 512 образцов (*батч* - набор, пакет данных). Это означает, что мы сделаем 40 итераций (или проходов) по всем образцам данных в тензорах `x_train` и `y_train`. После обучения мы узнаем потери и точность нашей модели, показав ей 10,000 образцов из проверочного набора данных:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "tXSGrjWZ-llW"
   },
   "outputs": [],
   "source": [
    "history = model.fit(partial_x_train,\n",
    "                    partial_y_train,\n",
    "                    epochs=40,\n",
    "                    batch_size=512,\n",
    "                    validation_data=(x_val, y_val),\n",
    "                    verbose=1)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "9EEGuDVuzb5r"
   },
   "source": [
    "## Оценим точность модели\n",
    "\n",
    "Теперь когда обучение прошло успешно, давай посмотрим какие результаты показывает модель.\n",
    "\n",
    "Она будет возвращать 2 значения: потери *loss* (число, которое показывает ошибку, чем оно ниже, тем лучше), и точность *accuracy*."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "zOMKywn4zReN"
   },
   "outputs": [],
   "source": [
    "results = model.evaluate(test_data, test_labels)\n",
    "\n",
    "print(results)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "z1iEXVTR0Z2t"
   },
   "source": [
    "Как мы видим, этот достаточно наивный подход достиг точности около 87%. Если бы мы использовали более сложные методы, то модель приблизилась бы к отметке в 95%."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "5KggXVeL-llZ"
   },
   "source": [
    "## Построим временной график точности и потерь\n",
    "\n",
    "Метод `model.fit()` возвращает объект `History`, который содержит все показатели, которые были записаны в лог во время обучения:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "VcvSXvhp-llb"
   },
   "outputs": [],
   "source": [
    "history_dict = history.history\n",
    "history_dict.keys()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "nRKsqL40-lle"
   },
   "source": [
    "Здесь всего четыре показателя, по одному для каждой отслеживаемой метрики во время обучения и проверки. Мы можем использовать их, чтобы построить графики потерь и точности обоих стадий для сравнения: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "nGoYf2Js-lle"
   },
   "outputs": [],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "acc = history.history['acc']\n",
    "val_acc = history.history['val_acc']\n",
    "loss = history.history['loss']\n",
    "val_loss = history.history['val_loss']\n",
    "\n",
    "epochs = range(1, len(acc) + 1)\n",
    "\n",
    "# \"bo\" означает \"blue dot\", синяя точка\n",
    "plt.plot(epochs, loss, 'bo', label='Потери обучения')\n",
    "# \"b\" означает \"solid blue line\", непрерывная синяя линия\n",
    "plt.plot(epochs, val_loss, 'b', label='Потери проверки')\n",
    "plt.title('Потери во время обучения и проверки')\n",
    "plt.xlabel('Эпохи')\n",
    "plt.ylabel('Потери')\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "colab": {
     "autoexec": {
      "startup": false,
      "wait_interval": 0
     }
    },
    "colab_type": "code",
    "id": "6hXx-xOv-llh"
   },
   "outputs": [],
   "source": [
    "plt.clf()   # Очистим график\n",
    "acc_values = history_dict['acc']\n",
    "val_acc_values = history_dict['val_acc']\n",
    "\n",
    "plt.plot(epochs, acc, 'bo', label='Точность обучения')\n",
    "plt.plot(epochs, val_acc, 'b', label='Точность проверки')\n",
    "plt.title('Точность во время обучения и проверки')\n",
    "plt.xlabel('Эпохи')\n",
    "plt.ylabel('Точность')\n",
    "plt.legend()\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "colab_type": "text",
    "id": "oFEmZ5zq-llk"
   },
   "source": [
    "\n",
    "На графиках точками отмечены потери и точность модели во время обучения, а линией - во время проверки.\n",
    "\n",
    "Обрати внимание что потери во время обучения *уменьшаются*, а точность - *увеличивается* с каждой следующей эпохой. Это вполне ожидаемо, поскольку мы используем градиентный спуск *gradient descent* - он минимизирует показатели потерь с каждой итерацией настолько быстро, насколько это возможно.\n",
    "\n",
    "Но это совсем не тот случай если мы посмотрим на потери и точность во время проверки модели: после приблизительно 20 эпох они находятся на пике. Это явный пример переобучения: модель показывает более лучшие показатели на данных для обучения, нежели на новых, которых она еще не видела. После этого момента модель начинает переоптимизироваться и обучается представлениям, которые *являются свойственны* только данным для обучения. Таким образом, модель не учится *обобщать* новые, проверочные данные.\n",
    "\n",
    "Именно здесь мы можем предотвратить переобучение просто прекратив тренировку сразу после 20 эпох обучения. Далее мы посмотрим, как это можно сделать автоматически при помощи *callback*, функции обратного вызова."
   ]
  }
 ],
 "metadata": {
  "colab": {
   "collapsed_sections": [],
   "default_view": {},
   "name": "basic-text-classification.ipynb",
   "private_outputs": true,
   "provenance": [],
   "toc_visible": true,
   "version": "0.3.2",
   "views": {}
  },
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}
